#ifndef FILECLIENT_H
#define FILECLIENT_H


#include <iostream>
#include "Client.h"
#include <pthread.h>
#include <vector>

using namespace std;

#include "muscle/message/Message.h"

using namespace muscle;

struct OpenFile{
	char name[123];
	int mode;
	int fd;
};




class FileClient{

Client* m_client;

vector<OpenFile> OFT;

int fdcount;

public:

FileClient(Client* client){
	m_client = client;
}


 void Open(char* name, int mode, bool create){

	String fileOperation ("FileOperation");
	String fileOp ("Open");
	String parameter1 ("FileName");
	String param1 = name;
	String parameter2 ("Mode");
	int32 param2 = mode;
	String parameter3 ("Create");
	bool param3 = create;
	

	Message m;
	
	if (m.AddString(fileOperation, fileOp) != B_NO_ERROR) 
		{
	        	printf("ERROR: Couldn't call AddString\n");
	    	}

	if (m.AddString(parameter1, param1) != B_NO_ERROR) 
		{
	        	printf("ERROR: Couldn't call AddString\n");
	    	}

	if (m.AddInt32(parameter2, param2) != B_NO_ERROR) 
		{
	        	printf("ERROR: Couldn't call AddInt32\n");
	    	}

	if (m.AddBool(parameter3, param3) != B_NO_ERROR) 
		{
	        	printf("ERROR: Couldn't call AddBool\n");
	    	}		

	uint32 size;
	size = m.FlattenedSize();

	m_client->Send (&size, sizeof(uint32));

	uint8 buffer[size];
	m.Flatten(buffer);
	m_client->Send (buffer, (int)size);
	//end of message sending

	int fd;

	
	if(m_client->Receive(&fd, sizeof(int)) == -1){
	  cout<< "Error Receiving File Descriptor"<< endl;
	}

	cout<< "the fd for this file is: " << fd<< endl;
	if (fd != -1){
		OpenFile of;
		strcpy(of.name,name); 
		of.mode = mode;
		of.fd = fd;
		OFT.push_back(of);
	}
	else{
		cout<< "Error Opening File" << endl;
	}
}

void Close (char* name){
	int file = SearchOFT(OFT, name);
	int fd = OFT[file].fd;	
	cout<<"the fd of the file is: " << fd << endl;

	String fileOperation ("FileOperation");
	String fileOp ("Close");
	String parameter1("FD");
	int32 param1 = fd;

	Message m;
	
	if (m.AddString(fileOperation, fileOp) != B_NO_ERROR){
	        	printf("ERROR: Couldn't call AddString\n");
	    	}

	if (m.AddInt32(parameter1, param1) != B_NO_ERROR){
	        	printf("ERROR: Couldn't call AddString\n");
	    	}

	uint32 size;
	size = m.FlattenedSize();

	m_client->Send (&size, sizeof(uint32));

	uint8 buffer[size];	
	m.Flatten(buffer);
	m_client->Send (buffer, (int)size);
	//end of message sending

	int success;

	if(m_client->Receive(&success, sizeof(int)) == -1){
	  cout<< "Error Receiving Success variable"<< endl;
	}
	if (success == 0){
		OFT.erase(OFT.begin() + file);
		cout<<name<<" closed"<<endl;
	}
	else{
		cout<<"file close failed"<<endl;		
	}
}

	
 void Write (char* name, char* text, int bytes){
	int fd;
	int mode;
	int file = SearchOFT(OFT, name);
	if(file > -1){
		fd = OFT[file].fd;
		mode = OFT[file].mode;
	}
	else{
		cout<< name << " is not open or does not exist" << endl;
		return;
	}

	if (mode == 2){
		String fileOperation ("FileOperation");
		String fileOp ("Write");
		String parameter1 ("FD");
		int param1 = fd;
		String parameter2  ("Length");
		int param2 = bytes;

		Message m;
		
		if (m.AddString(fileOperation, fileOp) != B_NO_ERROR) 
			{
		        	printf("ERROR: Couldn't call AddString\n");
		    	}
	
		if (m.AddInt32(parameter1, param1) != B_NO_ERROR) 
			{
		        	printf("ERROR: Couldn't call AddInt32\n");
		    	}
		
		if (m.AddInt32(parameter2,param2) != B_NO_ERROR) 
			{
		        	printf("ERROR: Couldn't call AddInt32\n");
		    	}
	
		uint32 size;
		size = m.FlattenedSize();
	
		m_client->Send (&size, sizeof(uint32));
	
		uint8 buffer[size];	
		m.Flatten(buffer);
		m_client->Send (buffer, (int)size);

		m_client->Send(text, bytes);
		//end of message sending

		int checkSize;

		if(m_client->Receive(&checkSize, sizeof(int))== -1){
		  cout<< "Error Receiving"<< endl;
		}
		
		if (checkSize != bytes){
			cout<< "Error Writing to Server"<<endl;
		}
	
		else{
			cout<<"Wrote "<<checkSize<< " bytes to file"<<endl;
		}
	}

	else{
	cout<<"File is not open for writing."<<endl;
	}
}

 int Read (string name, int length, char* ReadBuffer){

	int fd;
	int mode;
	int messageSize;
	int file = SearchOFT(OFT, name);
	if(file > -1){
		fd = OFT[file].fd;
		mode = OFT[file].mode;
	}
	else{
		cout<< name << " is not open or does not exist" << endl;
		return -1;
	}
	
	if (mode == 1){
		String fileOperation ("FileOperation");
		String fileOp ("Read");
		String parameter1 ("FD");
		int param1 = fd;
		String parameter2  ("Length");
		int param2 = length;
		String Buffer ("Buffer");

		Message m;
		
		if (m.AddString(fileOperation, fileOp) != B_NO_ERROR) 
			{
		        	printf("ERROR: Couldn't call AddString\n");
		    	}
	
		if (m.AddInt32(parameter1, param1) != B_NO_ERROR) 
			{
		        	printf("ERROR: Couldn't call AddInt32\n");
		    	}
		
		if (m.AddInt32(parameter2, param2) != B_NO_ERROR) 
			{
		        	printf("ERROR: Couldn't call AddInt32\n");
		    	}
	
		uint32 size;
		size = m.FlattenedSize();
	
		m_client->Send (&size, sizeof(uint32));
	
		uint8 buffer[size];	
		m.Flatten(buffer);
		m_client->Send (buffer, (int)size);
		//end of message sending



		if(m_client->Receive(&messageSize, sizeof(int)) > -1){	//recieve message size

			if(messageSize > 0){
				char fileText[messageSize];		

				if(m_client->Receive(ReadBuffer, messageSize) == -1){
					cout<< "Error Receiving" << endl;
				}
				//cout.write(ReadBuffer, messageSize);
				//cout<<endl;
				cout.flush();
			}
			else{
				cout<< "Error reading or Reached EOF" << endl;
			}
		}
		
		else{
			cout<<"Error finding message buffer."<<endl;
		}
	}
	else{
	cout<<"File is not open for reading"<<endl;
	}
	return messageSize;
}

void Exists(char* name){
	String fileOperation ("FileOperation");
	String fileOp ("Exists");
	String parameter1("FileName");
	String param1 = name;

	Message m;
	
	if (m.AddString(fileOperation, fileOp) != B_NO_ERROR){
	        	printf("ERROR: Couldn't call AddString\n");
	    	}

	if (m.AddString(parameter1, param1) != B_NO_ERROR){
	        	printf("ERROR: Couldn't call AddString\n");
	    	}

	uint32 size;
	size = m.FlattenedSize();

	m_client->Send (&size, sizeof(uint32));

	uint8 buffer[size];	
	m.Flatten(buffer);
	m_client->Send (buffer, (int)size);
	//end of message sending

	int checkFound;

	if(m_client->Receive(&checkFound, sizeof(int))== -1){
		cout<< "Error Receiving"<< endl;
	}

	if (checkFound == 1){
		cout<<"File Exists."<<endl;
	}
	else{
		cout<<"File does not exist."<<endl;
	}
	
}

void Delete (char* name){
	String fileOperation ("FileOperation");
	String fileOp ("Delete");
	String parameter1("FileName");
	String param1 = name;

	Message m;
	
	if (m.AddString(fileOperation, fileOp) != B_NO_ERROR) {
	        	printf("ERROR: Couldn't call AddString\n");
	    	}

	if (m.AddString(parameter1, param1) != B_NO_ERROR) {
	        	printf("ERROR: Couldn't call AddString\n");
	    	}

	uint32 size;
	size = m.FlattenedSize();

	m_client->Send (&size, sizeof(uint32));

	uint8 buffer[size];	
	m.Flatten(buffer);
	m_client->Send (buffer, (int)size);
	//end of message sending
		
	int error;

	if(m_client->Receive(&error, sizeof(int))== -1){
		cout<< "Error Receiving"<< endl;
	}

	if (error == -1){
		cout<<"File not fouund."<<endl;
	}
	else{
		cout<< "File deleted" << endl;
		string Name = name;
		int index = SearchOFT(OFT, Name);
		
		OFT.erase(OFT.begin() + index);
	}
	
}

void List(){

	String fileOperation ("FileOperation");
	String fileOp ("List");
	String FileName ("FileName");
	Message m;	

	if (m.AddString(fileOperation, fileOp) != B_NO_ERROR){
	        	printf("ERROR: Couldn't call AddString\n");
	    	}

	int Size; //needs to be uint32?
	Size = m.FlattenedSize();

	m_client->Send (&Size, sizeof(int)); //send over the size of the flattened message

	uint8 buffer[Size];
	m.Flatten(buffer);
	m_client->Send (buffer, Size);//send over the "List" message
	//end of send

	int entries;

	if(m_client->Receive(&entries, sizeof(int)) == -1){
	  cout<< "Error Receiving"<< endl;
	}
	
	cout << "directory has " << entries << " entries" <<endl;
		
	String size ("Size");
	int fileSize;
	Message fileMessage;
	String fileContents;
	int ack = 1;
	for (int i = 0; i < entries; i++){
		int ACK = 1;
		int messageSize;
		if(m_client->Receive(&messageSize, sizeof(int)) == -1){	//recieve message size
			cout<< "Error Receiving message size"<< endl;
		}
		
		uint8 buff[messageSize];
		if(m_client->Receive(buff, messageSize)== -1){ //recieve message buffer
			cout<< "Error Receiving message buffer"<< endl;
		}

		if(fileMessage.Unflatten(buff, messageSize)==B_ERROR){ //unflatten buffer
			cout<< "Error unflattening message" << endl;
		}

		if(fileMessage.FindString(FileName, fileContents)== B_ERROR){//Read contents out of message	
			cout<<"Error finding message buffer."<<endl;
		}	

		if(fileMessage.FindInt32(size, (int32*)&fileSize) == B_ERROR){
			cout<<"Error finding file size."<<endl;	
		}
		cout<< fileContents.Cstr()<< " "<< fileSize<< endl;
		
	} 

}


void Exit(){

	String fileOperation ("FileOperation");
	String fileOp ("Exit");
	Message m;	

	if (m.AddString(fileOperation, fileOp) != B_NO_ERROR){
	        	printf("ERROR: Couldn't call AddString\n");
	    	}

	uint32 size;
	size = m.FlattenedSize();

	m_client->Send (&size, sizeof(uint32));

	uint8 buffer[size];
	m.Flatten(buffer);
	m_client->Send (buffer, (int)size);

	int success;

	if(m_client->Receive(&success, sizeof(int)) == -1){
	  cout<< "Error Receiving"<< endl;
	}

}
static int SearchOFT(vector<OpenFile> OFT, string name){
		for(int i =0; i< OFT.size(); i++){
			if(strcmp(OFT[i].name,name.c_str()) == 0){
				return i;
			}
		}
		return -1;
	}
};

#endif

/*
static void SendMessage(string message, Client* client){

	String FileOperation ("FileOperation");
	String Message1 = message.c_str();	
	Message m;
	
	if (m.AddString(FileOperation, Message1) != B_NO_ERROR) 
	{
        	printf("ERROR: Couldn't call AddString\n");
    	}

    
	uint32 size;
	size = m.FlattenedSize();

	cout<<"Made it to Send"<< endl;
	cout<< size<<endl;
	client->Send (&size, sizeof(uint32));

	uint8 buffer[size];
	m.Flatten(buffer);
	
	cout<<buffer<<endl;
	cout<< (int)size << endl;
	client->Send (buffer, (int)size);

	uint32 messageSize;

	if(client->Receive(&messageSize, sizeof(int)) == -1){
	  cout<< "Error Receiving"<< endl;
	}

	uint8 checkBuffer[messageSize];

	if(client->Receive(checkBuffer, messageSize)== -1){
	  cout<< "Error Receiving"<< endl;
	}

	Message checkMessage;

	if(checkMessage.Unflatten(checkBuffer, messageSize)==B_ERROR){
	  cout<< "Error flattening" << endl;
	}

	String CheckMes;

	if(checkMessage.FindString(FileOperation, CheckMes)==B_ERROR){
	  cout<< "Could not find String"<< endl;
	}
	else{
	  if(strcmp(message.c_str(),CheckMes.Cstr() )==0){
	    cout<<"Message Received" << endl;
	  }
	  else{
	    cout<<"Message Error" << endl;
	  }
	}
*/

